package com.company.hrmadmin.modules.interceptor;

import com.company.hrmadmin.common.constant.ErrorEnum;
import com.company.hrmadmin.common.constant.GlobalConstant;
import com.company.hrmadmin.common.exception.GlobalException;
import com.company.hrmadmin.common.util.CookieUtil;
import com.company.hrmadmin.common.util.HostHolder;
import com.company.hrmadmin.common.util.RedisKeyUtil;
import com.company.hrmadmin.common.util.RedisUtil;
import com.company.hrmadmin.modules.domain.LoginToken;
import com.company.hrmadmin.modules.domain.User;
import com.company.hrmadmin.modules.mapper.UserMapper;
import com.company.hrmadmin.modules.service.LoginTokenService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

/**
 * 登录拦截器
 *
 * @author Song, Wei
 * Date: 2022/4/21 16:26
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private HostHolder hostHolder;
    @Autowired
    private LoginTokenService loginTokenService;
    @Resource
    private UserMapper userMapper;

    /**
     * 预处理回调方法，实现处理器的预处理
     * 返回值：true表示继续流程；false表示流程中断，不会继续调用其他的拦截器或处理器
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 处理OPTIONS请求(预检请求)，具体看参阅“参考链接>>>http的协议的跨域cors 和 options请求的一些理解”
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "86400");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Content-Type", "text/html;charset=utf-8");
        if (request.getMethod().equals("OPTIONS")) {
            response.setStatus(HttpServletResponse.SC_OK);
            return false;
        }
        // token信息校验
        String token = request.getHeader(GlobalConstant.TOKEN_NAME);
        if (StringUtils.isNotBlank(token)) {
            // 表示存在登录凭证
            LoginToken loginToken = loginTokenService.getLoginToken(token);
            // 检查登录凭证是否失效
            if (loginToken != null && loginToken.getStatus() == 0 & loginToken.getExpiredTime().after(new Date())) {
                if (redisUtil.getCacheObject(RedisKeyUtil.getTokenKey(token)) != null) {
                    // 根据凭证查询用户
                    User user = userMapper.selectById(loginToken.getUserId());
                    // 在本次请求中持有用户，使用 ThreadLocal 存储用户
                    hostHolder.setUser(user);
                    // redis校验token信息
                    return true;
                } else {
                    throw new GlobalException(ErrorEnum.USER_NOT_EXIST);
                }
            }
        } else {
            throw new GlobalException(ErrorEnum.TOKEN_EXPIRED_ERROR);
        }
        return false;
    }

    /**
     * 后处理回调方法，实现处理器（controller）的后处理，但在渲染视图之前
     * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    }

    /**
     * 整个请求处理完毕回调方法，即在视图渲染完毕时回调，
     * 如性能监控中我们可以在此记录结束时间并输出消耗时间，
     * 还可以进行一些资源清理，类似于try-catch-finally中的finally，
     * 但仅调用处理器执行链中
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

